<script setup lang="ts">
  import { computed, CSSProperties, PropType, ref, watch } from 'vue'
  import { AiEditor } from 'aieditor'
  import 'aieditor/dist/style.css'
  import { deleteFile } from '@/api/upload'
  import { toolBarConfig, textSelectionBubbleMenuConfig } from './config'
  import { EditorConfig } from './types'

  const props = defineProps({
    placeholder: {
      type: String,
      default: () => '请输入内容...',
    },
    value: {
      type: String,
      default: () => '',
    },
    mainStyle: {
      type: Object as PropType<CSSProperties>,
      default: () => {
        return {}
      },
    },
    isAutoDeleteFile: {
      type: Boolean,
      default: () => true,
    },
    readonly: {
      type: Boolean,
      default: () => false,
    },
    isCanUploadImg: {
      type: Boolean,
      default: () => true,
    },
    isCanUploadVideo: {
      type: Boolean,
      default: () => true,
    },
    delFunction: {
      type: Array as PropType<Array<EditorConfig>>,
      default: () => [],
    },
  })

  // 编辑器挂载节点元素
  const editorRef = ref<null | HTMLDivElement>(null)

  // 编辑器实例
  let editorInstance: AiEditor | null = null

  // 上传文件列表
  const uploadFiles = ref<Array<{ tempUrl: string; file: File }>>([])

  /**
   * 内容保存之后文件列表 - 存放的是文件链接
   * 作用是：为获取删除文件列表提供一个对比列表
   * 获取删除文件列表步骤：
   * 1、在获取了编辑器内容后，将里面的文件链接提取出来
   * 2、监听编辑器内容的变化，如果文件链接不存在于编辑器内容里就证明文件已经被删除了，然后就将文件链接放到删除列表里
   */
  const editorFiles = ref<Array<string>>([])

  // 编辑器内容
  const editorValue = ref(props.value)

  /**
   * 获取菜单配置
   */
  const getToolBarConfig = computed(() => {
    let result = [...toolBarConfig]
    if (!props.isCanUploadImg) {
      result = result.filter((item) => item !== 'image')
    }

    if (!props.isCanUploadVideo) {
      result = result.filter((item) => item !== 'video')
    }

    if (props.readonly) {
      return []
    }

    return result
  })

  /**
   * 挂载编辑器
   */
  const mountEditor = () => {
    if (editorRef.value) {
      editorInstance = new AiEditor({
        // 挂载的节点
        element: editorRef.value,

        // 占位符
        placeholder: props.placeholder,

        // 内容
        content: editorValue.value,

        // 浮动菜单配置
        textSelectionBubbleMenu: {
          enable: !props.readonly,
          items: textSelectionBubbleMenuConfig,
        },

        // 工具栏配置
        toolbarKeys: getToolBarConfig.value.filter((item) => !props.delFunction.includes(item)),

        // 图片上传配置
        image: {
          uploader: (file: File) => {
            return new Promise((resolve) => {
              const tempUrl = URL.createObjectURL(file)
              uploadFiles.value.push({
                tempUrl,
                file,
              })
              resolve({
                errorCode: 0,
                data: {
                  src: tempUrl,
                  alt: '图片 alt',
                },
              })
            })
          },
        },

        // 视频上传配置
        video: {
          uploader: (file: File) => {
            return new Promise((resolve) => {
              const tempUrl = URL.createObjectURL(file)
              uploadFiles.value.push({
                tempUrl,
                file,
              })
              resolve({
                errorCode: 0,
                data: {
                  src: tempUrl,
                },
              })
            })
          },
        },

        // 编辑器内容更新事件
        onChange: (aiEditor) => {
          uploadFiles.value = uploadFiles.value.filter(
            (file) => aiEditor.getHtml().indexOf(file.tempUrl) !== -1,
          )
        },
      })
    }

    editorInstance.setEditable(!props.readonly)
  }

  /**
   * 销毁编辑器
   */
  const destoryEditor = () => {
    if (editorInstance) {
      editorInstance.destroy()
    }
  }

  /**
   * 获取编辑器的内容
   */
  const getEditorValue = async () => {
    if (editorInstance) {
      const deletedFiles = getDeletedFiles()
      if (props.isAutoDeleteFile && deletedFiles.length > 0) {
        await deleteFile(deletedFiles)
      }
      return editorInstance.getHtml()
    }
    return ''
  }

  /**
   * 获取上传文件列表
   */
  const getUploadFiles = () => {
    return uploadFiles.value
  }

  /**
   * 将临时路径转换成服务器的地址
   * @param {String} tempPath - 临时路径
   * @param {String} serverPath - 服务器路径
   */
  const convertTempPathToServerPath = async (tempPath: string, serverPath: string) => {
    if (editorInstance) {
      const val = editorInstance.getHtml().replace(tempPath, serverPath)
      editorInstance.focus().setContent(val)
    }
  }

  /**
   * 获取编辑器里的文件路径
   */
  const getEditorFile = () => {
    if (editorInstance) {
      const jsonObj = editorInstance.getJson()
      jsonObj.content.forEach((item) => {
        if (['video', 'image'].includes(item.type)) {
          editorFiles.value.push(item.attrs.src)
        }
      })
    }
  }

  /**
   * 获取删除过后文件列表
   */
  const getDeletedFiles = () => {
    const result: Array<string> = []
    if (editorInstance) {
      editorFiles.value.forEach((fileUrl) => {
        if (editorInstance.getHtml().indexOf(fileUrl) === -1) {
          result.push(fileUrl)
        }
      })
    }
    return result
  }

  watch(
    () => props.value,
    (value) => {
      editorValue.value = value
      if (editorInstance) {
        editorInstance.focus().setContent(value)
        getEditorFile()
      }
    },
    { immediate: true },
  )

  defineExpose({
    mountEditor,
    destoryEditor,
    getEditorValue,
    getUploadFiles,
    getDeletedFiles,
    convertTempPathToServerPath,
  })
</script>

<template>
  <div ref="editorRef" class="ai_editor_container">
    <div class="aie-container">
      <div class="aie-container-header"></div>
      <div class="aie-container-main" :style="mainStyle"></div>
      <div class="aie-container-footer"></div>
    </div>
  </div>
</template>

<style scoped lang="scss">
  .ai_editor_container {
    height: 100%;
    width: 100%;

    .aie-container {
      height: 100%;
      display: flex;
      flex-direction: column;

      .aie-container-main {
        flex: 1;
        overflow: auto;
        height: 0;
      }

      .aie-container-footer {
        display: none;
      }
    }
  }
</style>
